package helper

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/md5"
	"encoding/base64"
	"encoding/hex"
	"fmt"
	"github.com/spaolacci/murmur3"
	"io/ioutil"
	"strconv"
	"strings"
	"time"
	//"github.com/xxtea/xxtea-go/xxtea"
)

func MurHash(str string, key uint32) uint64 {
	h64 := murmur3.New64WithSeed(key)
	h64.Write([]byte(str))

	v := h64.Sum64()
	h64.Reset()
	return v
}

/*
func XxTeaEncrypt(str, key string) string {
	enStr := xxtea.EncryptString(str, key)
	return enStr
}

func XxteaDecrypt(str, key string) string {
	deStr, err := xxtea.DecryptString(str, key)
	if err != nil {
		return ""
	}
	return deStr
}
*/

func padKey(key string) []byte {
	keyBytes := []byte(key)
	if len(keyBytes) < 16 {
		for i := len(keyBytes); i < 16; i++ {
			keyBytes = append(keyBytes, byte(0))
		}
	} else if len(keyBytes) < 24 {
		for i := len(keyBytes); i < 24; i++ {
			keyBytes = append(keyBytes, byte(0))
		}
	} else if len(keyBytes) < 32 {
		for i := len(keyBytes); i < 32; i++ {
			keyBytes = append(keyBytes, byte(0))
		}
	}
	return keyBytes
}

func Encry(str string, key string) string {
	strBytes := []byte(str)
	keyBytes := padKey(key)

	block, err := aes.NewCipher(keyBytes)
	if err != nil {
		panic(err.Error())
	}

	cipherText := make([]byte, aes.BlockSize+len(strBytes))
	iv := cipherText[:aes.BlockSize]

	stream := cipher.NewCFBEncrypter(block, iv)
	stream.XORKeyStream(cipherText[aes.BlockSize:], strBytes)

	return base64.StdEncoding.EncodeToString(cipherText)
}

func Decry(str string, key string) string {
	strBytes, err := base64.StdEncoding.DecodeString(str)
	if err != nil {
		panic(err.Error())
	}

	keyBytes := padKey(key)
	block, err := aes.NewCipher(keyBytes)
	if err != nil {
		panic(err.Error())
	}

	iv := strBytes[:aes.BlockSize]
	strBytes = strBytes[aes.BlockSize:]

	stream := cipher.NewCFBDecrypter(block, iv)
	stream.XORKeyStream(strBytes, strBytes)

	return string(strBytes)
}

func ThinkEncrypt(data string, key string, expire int64) string {
	if key == "" {
		key = "crypt"
	}
	key = fmt.Sprintf("%x", md5.Sum([]byte(key)))
	data = base64.StdEncoding.EncodeToString([]byte(data))
	var char string
	x := 0
	for i := 0; i < len(data); i++ {
		if x == len(key) {
			x = 0
		}
		char += string(key[x])
		x++
	}
	var str string
	if expire == 0 {
		str = fmt.Sprintf("%010d", expire)
	} else {
		str = fmt.Sprintf("%010d", expire+time.Now().Unix())
	}
	for i := 0; i < len(data); i++ {
		str += convertIfNeeded(rune(int(data[i]) + (int(char[i]) % 256)))
	}
	base := base64.StdEncoding.EncodeToString([]byte(str))
	base = replaceCharacters(base)
	return base
}

func ThinkDecrypt(data string, key string) string {
	if len(key) == 0 {
		key = "crypt"
	}
	hashedKeyBytes := md5.Sum([]byte(key))
	hashedKey := hex.EncodeToString(hashedKeyBytes[:])

	// Data
	data = strings.Replace(data, "-", "+", -1)
	data = strings.Replace(data, "_", "/", -1)
	pad := len(data) % 4
	if pad > 0 {
		data += strings.Repeat("=", 4-pad)
	}
	decodedData, _ := base64.StdEncoding.DecodeString(data)

	expire := string(decodedData[:10])
	cryptData := decodedData[10:]

	if et, _ := strconv.Atoi(expire); et > 0 && int64(et) < time.Now().Unix() {
		return ""
	}

	var decrypted string
	var char string

	x := 0
	for i := 0; i < len(cryptData); i++ {
		if x == len(hashedKey) {
			x = 0
		}
		char += string(hashedKey[x])
		x++
	}

	for i := 0; i < len(cryptData); i++ {
		if cryptData[i] < char[i] {
			decrypted += convertIfNeeded(rune(int(cryptData[i]) + 256 - (int(char[i]))))
		} else {
			decrypted += convertIfNeeded(rune(int(cryptData[i]) - (int(char[i]))))
		}
	}

	result, _ := base64.StdEncoding.DecodeString(decrypted)
	return string(result)
}

func replaceCharacters(str string) string {
	str = strings.Replace(str, "+", "-", -1)
	str = strings.Replace(str, "/", "_", -1)
	str = strings.Replace(str, "=", "", -1)

	return str
}

func convertIfNeeded(character rune) string {
	if character > 127 {
		return string([]byte{byte(character)})
	}
	return string(character)
}

// 解密函数
func decrypt(data []byte, passphrase string) ([]byte, error) {
	key := []byte(passphrase)
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}

	if len(data) < aes.BlockSize {
		return nil, fmt.Errorf("ciphertext too short")
	}
	iv := data[:aes.BlockSize]
	data = data[aes.BlockSize:]

	stream := cipher.NewCFBDecrypter(block, iv)
	stream.XORKeyStream(data, data)

	return data, nil
}

// 从文件中读取加密的配置
func LoadEncryptedConfig(filename string, passphrase string) ([]byte, error) {
	encodedData, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}
	encryptedData, err := base64.StdEncoding.DecodeString(string(encodedData))
	if err != nil {
		return nil, err
	}
	return decrypt(encryptedData, passphrase)
}
